#include <my_global.h>
#include <mysql.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <uuid/uuid.h>
#include <pthread.h>
#include <time.h>
#include "model.h"
#include "mydata.h"
#include "dbhelp.h"

static int fetch_config(char *host, char *uname, char *password);
static int connection(char *host, char *uname, char *password);
static int dbinit();
static int session_remove(const char *uuid);
static int local_time(char *now);
static int session_exist2(uint32_t user_id, char *uuid);
static int gen_uuid(char *uuid);
static int check_note(Note *note);
static int authortity_check(uint32_t user_id, uint64_t note_id);

static inline int result_rows(const char *query);

// inline help functions.
static inline int get_number_mysql(char *query, char *num);
static inline int user_copy_mysql(char *query, ArrayTable *dest);
static inline int note_copy_mysql(char *query, ArrayTable *dest);
static inline int tag_copy_mysql(char *query, ArrayTable *dest);

// database globals.
static char host[64],user[32],password[64];
static char mydata[] = "netbook";
static MYSQL *conn;
static pthread_mutex_t mutex_mysql;

/** mydata **/
static int
init(void *ctx)
{	
    dbinit();
    return 0;
}

static int
end(void)
{
    mysql_close(conn);
    pthread_mutex_destroy(&mutex_mysql);
    return 0;
}

/** Session **/
static const char *session_items = "uuid,user_id,time_create,attribute";
static const char *session_insert = "INSERT INTO Session(%s) VALUES ('%s', '%d', '%s', '%d')";
// insert
static int
insert_session(Session *session)
{
    uint32_t my_id;
    char uuid[37];
    char query[256];
    mytime t;

    gen_uuid(uuid);
    local_time(t);
    sprintf(query, session_insert, session_items, uuid, session->user_id, t, session->attribute);

    LOG(query);
    SAFE_QUERY(query, -1);
    // setting session.
    session->uuid = strdup(uuid);
    session->time_create = strdup(t);
    // update related user.
    sprintf(query, "UPDATE User SET time_lastlogin = '%s' WHERE id = '%d' ", t, session->user_id);
    LOG(query);
    SAFE_QUERY(query, -1);
    return 0;
}

// remove
static int
remove_session(const char *uuid)
{
    char query[128];
    sprintf(query, "DELETE FROM Session WHERE uuid = '%s' ", uuid);
    LOG(query);
    SAFE_QUERY(query, -1);
    return 0;
}

// verify
static int
session_exist(const char *uuid, uint32_t *user_id)
{
    MYSQL_RES *result;
    MYSQL_ROW row;
    char query[256];
    sprintf(query, "SELECT user_id FROM Session WHERE uuid = '%s' ", uuid);
    LOG(query);
    SAFE_QUERY_STORE(query, result, NO);
    if (!(row = mysql_fetch_row(result))) {
	mysql_free_result(result);
	return NO;
    }
    if (user_id) {
	*user_id = atoll(row[0]);}
    mysql_free_result(result);
    return YES;
}

static int
session_user_exist(uint32_t user_id, char **uuid)
{
    MYSQL_RES *result;
    MYSQL_ROW row;
    char query[256];
    sprintf(query, "SELECT uuid FROM Session WHERE user_id = '%d' ", user_id);
    LOG(query);
    SAFE_QUERY_STORE(query, result, NO);
    if (!(row = mysql_fetch_row(result))) {
	mysql_free_result(result);
	return NO;
    }
    *uuid = strdup(row[0]);
    mysql_free_result(result);
    return YES;
}

// other

/** User **/
static const char *user_items = "id,nickname,mail,passwd,time_reg,time_lastlogin,time_lastupdate,attribute ";
// insert, update, remove
static const char *user_insert = "INSERT INTO User(%s) VALUES(NULL,'%s','%s','%s','%s','%s','%s','%d')";
static const char *user_update = "UPDATE User SET %s %s %s %s time_lastupdate = '%s' WHERE id = '%d' ";
static const char *user_remove = "DELETE FROM User WHERE id = '%d' ";
static int
insert_user(User *user)
{
    char query[1024];
    mytime t;
    local_time(t);
    sprintf(query, user_insert, user_items, user->nickname, user->mail, user->passwd, t, t, t, user->attribute);
    LOG(query);
    uint64_t id;
    SAFE_QUERY_INSERT_ID(query, id, -1);
    // setting user.
    user->id = id;
    user->time_reg = strdup(t);
    user->time_lastlogin = strdup(t);
    user->time_lastupdate = strdup(t);
    return 0;
}

static int
update_user(User *user, uint32_t field)
{
    char query[1<<15]={0};
    char nickname[128]={0};
    char mail[128]={0};
    char passwd[123]={0};
    char attribute[50]={0};
    mytime t;
    local_time(t);
    if (field&USER_NICKNAME) {
	sprintf(nickname, "nickname='%s',", user->nickname); }
    if (field&USER_MAIL) {
	sprintf(mail, "mail ='%s',", user->mail); }
    if (field&USER_PASSWD) {
	sprintf(passwd, "passwd='%s',", user->passwd); }
    if (field&USER_ATTRIBUTE) {
	sprintf(attribute, "attribute='%d',", user->attribute); }
    sprintf(query, user_update, nickname, mail, passwd, attribute, t, user->id);
    LOG(query);
    if (mysql_query(conn, query)) {
	printf("Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
	return -1;
    }
    // setting user.

    return 0;
}

static int
remove_user(uint32_t user_id)
{
    char query[256];
    sprintf(query, user_remove, user_id);
    LOG(query);
    if (mysql_query(conn, query)) {
	printf("Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
	return -1;
    }
    return 0;
}

// verify
static int
user_mail_exist(const char *mail, uint32_t *user_id)
{
    MYSQL_RES *result;
    MYSQL_ROW row;
    char query[256];
    sprintf(query, "SELECT id FROM User WHERE mail = '%s' ", mail);
    LOG(query);
    SAFE_QUERY_STORE(query, result, NO);
    if (!(row = mysql_fetch_row(result))) {
	mysql_free_result(result);
	return NO;
    }
    if (user_id) {
	*user_id = atoll(row[0]);}
    mysql_free_result(result);
    return YES;
}

static int
mail_passwd_match(const char *mail, const char *passwd, uint32_t *user_id)
{
    MYSQL_RES *result;
    MYSQL_ROW row;
    char query[256];
    sprintf(query, "SELECT id FROM User WHERE mail = '%s' AND passwd = '%s' ", mail, passwd);
    LOG(query);
    SAFE_QUERY_STORE(query, result, NO);

    if (!(row = mysql_fetch_row(result))) {
	mysql_free_result(result);
	return NO;
    }
    if (user_id) {
	*user_id = atoll(row[0]);}
    mysql_free_result(result);
    return YES;
}

static int
id_passwd_match(uint32_t user_id, const char *passwd)
{
    MYSQL_RES *result;
    MYSQL_ROW row;
    char query[256];
    sprintf(query, "SELECT * FROM User WHERE id = '%d' AND passwd = '%s' ", user_id, passwd);
    return result_rows(query);
}
// get
static const char *id_user = "SELECT %s FROM User WHERE id = '%d' ";
static int
get_user_by_id(uint32_t user_id, User **user)
{
    char query[256];
    sprintf(query, id_user, user_items, user_id);

    ArrayTable array;
    user_copy_mysql(query, &array);
    if (array.size == 0) {
	return -1; }
    *user = (User *)(array.ptr);
    return 0;
}

static const char *mail_user = "SELECT %s FROM User WHERE mail = '%s' ";
static int
get_user_by_mail(const char *mail, User **user)
{
    char query[256];
    sprintf(query, mail_user, user_items, mail);

    ArrayTable array;
    user_copy_mysql(query, &array);
    if (array.size == 0) {
	return -1; }
    *user = (User *)(array.ptr);
    return 0;
}
static const char *list_user = "SELECT %s FROM User ";
static int
get_user_list(ArrayTable *result)
{
    char query[256];
    sprintf(query, list_user, user_items);
    user_copy_mysql(query, result);
    return 0;
}

/** Note **/
static const char *note_items = "id,owner_id,uuid,title,content,time_create,time_lastupdate,version,attribute "; 
// insert, update, remove
static const char *note_insert = "INSERT INTO Note(%s) VALUES(NULL,'%d','%s','%s','%s','%s','%s','%d','%d')";
static const char* note_update = "UPDATE Note SET %s %s %s %s %s time_lastupdate='%s' WHERE id = '%d' ";
static const char* note_remove = "DELETE FROM Note WHERE id = '%lld' ";

static int
insert_note(Note *note)
{
    mytime t;
    char query[1<<21];
    char uuid[37];
    local_time(t);
    gen_uuid(uuid);
    sprintf(query, note_insert, note_items, note->owner_id, uuid, note->title, note->content, t, t, note->version, note->attribute);
    LOG(query);
    uint64_t id;
    SAFE_QUERY_INSERT_ID(query, id, -1);
    // setting note.
    note->id = id;
    note->uuid = strdup(uuid);
    note->time_create = strdup(t);
    note->time_lastupdate = strdup(t);
    return 0;
}

static int
update_note(Note *note, uint32_t field)
{
    char query[1<<21]={0};
    char title[1<<8]={0};
    char content[1<<20]={0};
    char version[30]={0};
    char attribute[50]={0};
    char status[50]={0};
    mytime t;
    local_time(t);
    if (field&NOTE_TITLE) {
	sprintf(title, "title='%s',", note->title); }
    if (field&NOTE_CONTENT) {
	sprintf(content, "content='%s',", note->content); }
    if (field&NOTE_VERSION) {
	sprintf(version, "version='%d',", note->version); }
    if (field&NOTE_ATTRIBUTE) {
	sprintf(attribute, "attribute='%d',", note->attribute); }
    sprintf(query, note_update, title, content, version, attribute, status, t, note->id);
    LOG(query);
    if (mysql_query(conn, query)) {
	printf("Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
	return -1;
    }
    // setting note.
    return 0;
}

static int
remove_note(uint64_t note_id)
{
    char query[128];
    sprintf(query, note_remove, note_id);
    LOG(query);
    if (mysql_query(conn, query)) {
	printf("Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
	return -1;
    }
    return 0;
}

// verify
static int
own_note(uint32_t user_id, uint64_t note_id)
{
    char query[256];
    sprintf(query, "SELECT * FROM Note WHERE owner_id = '%d' AND id = '%lld' ", user_id, note_id);
    return result_rows(query);
}

static int
note_is_public(uint64_t note_id)
{
    char query[256];
    sprintf(query, "SELECT * FROM Note WHERE id = %lld AND attribute&%d=0 ", note_id, PUBLIC);
    return result_rows(query);
}

static int
note_is_shared(uint64_t note_id, uint32_t user_id)
{
    char query[256];
    sprintf(query, "SELECT * FROM Share WHERE note_id = %lld AND user_id = %d ", note_id, user_id);
    return result_rows(query);
}

// get
static const char *id_note ="SELECT %s FROM Note WHERE id = '%d' ";
static int
get_note_by_id(uint64_t note_id, Note **note)
{
    char query[256];
    sprintf(query, id_note, note_items, note_id);

    ArrayTable array;
    note_copy_mysql(query, &array);
    if (array.size == 0) {
	return -1; }
    *note = (Note *)(array.ptr);
    return 0;
}

static const char *uuid_note ="SELECT %s FROM Note WHERE uuid = '%s' ";
static int
get_note_by_uuid(const char *uuid, Note **note)
{
    char query[256];
    sprintf(query, uuid_note, note_items, uuid);

    ArrayTable array;
    note_copy_mysql(query, &array);
    if (array.size == 0) {
	return -1; }
    *note = (Note *)(array.ptr);
    return 0;
}

static int
get_all_public_note_number(uint64_t *number)
{
    char query[256];
    char num[50];
    sprintf(query, "SELECT count(*) FROM Note WHERE attribute&%d=0 ", PUBLIC);

    if (get_number_mysql(query, num) < 0) {
	return -1; }
    *number = atoll(num);
    return 0;
}

static const char *list_note_all_public ="SELECT %s FROM Note WHERE attribute&%d = 0 ORDER BY time_create DESC LIMIT %lld,%d ";
static int
get_all_public_note_list(uint64_t start, uint32_t count, ArrayTable *result)
{
    char query[256];
    sprintf(query, list_note_all_public, note_items, PUBLIC, start, count);
    note_copy_mysql(query, result);
    return 0;
}

static int
get_my_note_number(uint32_t user_id, uint64_t *number)
{
    char query[256];
    char num[50];
    sprintf(query, "SELECT count(*) FROM Note WHERE owner_id = %d ", user_id);

    if (get_number_mysql(query, num) < 0) {
	return -1; }
    *number = atoll(num);
    return 0;
}

static const char *list_note_my ="SELECT %s FROM Note WHERE owner_id = %d ORDER BY time_create DESC LIMIT %lld,%d ";
static int
get_my_note_list(uint32_t user_id, uint64_t start, uint32_t count, ArrayTable *result)
{
    char query[256];
    sprintf(query, list_note_my, note_items, user_id, start, count);
    note_copy_mysql(query, result);
    return 0;
}

/** Tag **/
static const char *tag_items = "name,note_id,owner_id,time_create ";
// insert, update, remove
static const char *tag_insert = "INSERT INTO Tag(%s) VALUES('%s','%lld','%d','%s') ";
static const char *tag_remove = "DELETE FROM Tag WHERE note_id = '%lld' AND name = '%s' ";
static const char *tags_remove = "DELETE FROM Tag WHERE note_id = '%lld' ";
static int
insert_tag(Tag *tag)
{
    char query[512];
    mytime t;
    local_time(t);
    sprintf(query, tag_insert, tag_items, tag->name, tag->note_id, tag->owner_id, t);
    LOG(query);
    SAFE_QUERY(query, -1);
    // setting user.
    tag->time_create = strdup(t);
    return 0;
}

static int
remove_tag(uint64_t note_id, const char *tag)
{
    char query[512];
    sprintf(query, tag_remove, note_id, tag);
    LOG(query);
    SAFE_QUERY(query, -1);
    return 0;
}

static int
remove_tags(uint64_t note_id)
{
    char query[512];
    sprintf(query, tags_remove, note_id);
    LOG(query);
    SAFE_QUERY(query, -1);
    return 0;
}

// verify
static int
tag_exist(uint64_t note_id, const char *tag)
{
    MYSQL_RES *result;
    MYSQL_ROW row;
    char query[256];
    sprintf(query, "SELECT * FROM Tag WHERE note_id = '%lld' AND name = '%s' ", note_id, tag);
    return result_rows(query);
}

// get
static const char *tags_get = "SELECT %s,0 FROM Tag WHERE note_id = '%lld' ";
static int
get_tags(uint64_t note_id, ArrayTable *result)
{
    char query[256];
    sprintf(query, tags_get, tag_items, note_id);
    tag_copy_mysql(query, result);
    return 0;
}

static const char *taglist_get = "SELECT %s,COUNT(name) AS ref FROM Tag WHERE owner_id = '%d' GROUP BY name, ORDER BY ref DESC";
static int
get_tag_list(uint32_t user_id, ArrayTable *result)
{
    char query[256];
    sprintf(query, taglist_get, tag_items, user_id);
    tag_copy_mysql(query, result);
    return 0;
}

/*
static int
get_my_note_number(uint32_t my_id, uint32_t *number)
{
    char query[128];
    char num[100];
    sprintf(query, "SELECT count(*) FROM Note WHERE user_id = '%u' ", my_id);

    if (get_number_mysql(query, num) >= 0) {
	*number = atol(num);
	return 0;
    }
    return -1;
}

static const char *my_note ="SELECT %s FROM Note WHERE user_id = '%u' ORDER BY create_time DESC LIMIT %u,%u ";
static int
get_my_note_list(uint32_t my_id, uint32_t start, uint32_t count, uint32_t field, NoteArray *result)
{
    char query[256];
    sprintf(query, my_note, note_items, my_id, start, count);

    notearray_copy_mysql(query, field, result);

    return 0;
}

static int
get_user_shared_note_number(uint32_t my_id, uint32_t user_id, uint32_t *number)
{
    char query[256];
    char num[100];

    sprintf(query, "SELECT count(*) FROM Shared WHERE owner_id = '%u' AND user_id = '%u' ", user_id, my_id);

    if (get_number_mysql(query, num) >= 0) {
	*number = atol(num);
	return 0;
    }
    return -1;
}

static const char *user_shared ="SELECT %s FROM Note WHERE id IN (SELECT note_id FROM Shared WHERE owner_id = '%u'  and user_id = '%u') ORDER BY create_time DESC LIMIT %u,%u ";
static int
get_user_shared_note_list(uint32_t my_id, uint32_t user_id, uint32_t start, uint32_t count, uint32_t field, NoteArray *result)
{
    char query[256];

    sprintf(query, user_shared, note_items, user_id, my_id, start, count);

    notearray_copy_mysql(query, field, result);

    return 0;
}

static int
get_all_shared_note_number(uint32_t my_id, uint64_t *number)
{
    char query[256];
    char num[100];

    sprintf(query, "SELECT count(*) FROM Shared WHERE user_id = '%u' ", my_id);

    if (get_number_mysql(query, num) >= 0) {
	*number = atoll(num);
	return 0;
    }
    return -1;
}

static const char *all_shared ="SELECT %s FROM Note WHERE id IN (SELECT note_id FROM Shared WHERE user_id = '%u') ORDER BY create_time DESC LIMIT %llu,%u ";
static int
get_all_shared_note_list(uint32_t my_id, uint64_t start, uint32_t count, uint32_t field, NoteArray *result)
{
    char query[256];

    sprintf(query, all_shared, note_items, my_id, start, count);

    notearray_copy_mysql(query, field, result);

    return 0;
}

static int
get_user_public_note_number(uint32_t user_id, uint32_t *number)
{
    char query[256];
    char num[100];

    sprintf(query, "SELECT count(*) FROM Note WHERE user_id = '%u' AND attribute&%u = %u ", user_id, PUBLIC, PUBLIC);

    if (get_number_mysql(query, num) >= 0) {
	*number = atol(num);
	return 0;
    }
    return -1;
}

static const char *user_public ="SELECT %s FROM Note WHERE user_id = '%u' AND attribute&%u = %u ORDER BY create_time DESC LIMIT %u,%u ";
static int
get_user_public_note_list(uint32_t user_id, uint32_t start, uint32_t count, uint32_t field, NoteArray *result)
{
    char query[256];

    sprintf(query, user_public, note_items, user_id, PUBLIC, PUBLIC, start, count);
            
    notearray_copy_mysql(query, field, result);

    return 0;
}

*/


struct mydata_ops mydata_real = {
    .name = "mydata_real",
    .desc = "mydata APIs version 0.0.1x",

    /* mydata */
    .init = init,
    .end = end,

    /* authentication */
    .insert_session  = insert_session,
    .remove_session = remove_session,
    .session_exist = session_exist,
    .session_user_exist = session_user_exist,

    /* user */
    .insert_user = insert_user,
    .update_user = update_user,
    .remove_user = remove_user,
    .user_mail_exist = user_mail_exist,
    .mail_passwd_match = mail_passwd_match,
    .id_passwd_match = id_passwd_match,
    .get_user_by_id = get_user_by_id,
    .get_user_by_mail = get_user_by_mail,
    .get_user_list = get_user_list,

    /* Note */
    .insert_note = insert_note,
    .update_note = update_note,
    .remove_note = remove_note,

    .own_note = own_note,
    .note_is_public = note_is_public,
    .note_is_shared = note_is_shared,
    .get_note_by_id = get_note_by_id,
    .get_note_by_uuid = get_note_by_uuid,
    .get_all_public_note_number = get_all_public_note_number,
    .get_all_public_note_list = get_all_public_note_list,
    .get_my_note_number = get_my_note_number,
    .get_my_note_list = get_my_note_list,

    /* Tag */
    .insert_tag = insert_tag,
    .remove_tag = remove_tag,
    .remove_tags = remove_tags,
    .tag_exist = tag_exist,
    .get_tags = get_tags,
    .get_tag_list = get_tag_list,
    /*
    .get_my_note_number = get_my_note_number,
    .get_my_note_list = get_my_note_list,

    .get_user_shared_note_number = get_user_shared_note_number,
    .get_user_shared_note_list = get_user_shared_note_list,

    .get_all_shared_note_number = get_all_shared_note_number,
    .get_all_shared_note_list = get_all_shared_note_list,

    .get_user_public_note_number = get_user_public_note_number,
    .get_user_public_note_list =  get_user_public_note_list,

    */

};

/*****************************************\
 * mydata private functions.
\*****************************************/
static int
dbinit()
{
    fetch_config(host, user, password);
    connection(host, user, password);
    pthread_mutex_init(&mutex_mysql, NULL);

    return 0;
}

static int
fetch_config(char *host, char *uname, char *password)
{
    FILE *config;
    char buf[128];
    int i;
    if ((config = fopen("db.config", "r")) == NULL) {
	fprintf(stderr, "Error: No CONFIG file!\n");
	exit(1);
    }

    while (fgets(buf,128,config)) {
	switch (buf[0]) {
	    case '#':
		break;
	    case 'H':
		if (strncmp(buf,"HOST=",5) == 0) {
		    strncpy(host, (buf+5), index(buf,'\n') - (buf + 5));
		}
		break;
	    case 'U':
		if (strncmp(buf,"USER=",5) == 0) {
		    strncpy(user, (buf+5), index(buf,'\n') - (buf + 5));
		}
		break;
	    case 'P':
		if (strncmp(buf,"PASSWORD=",9) == 0) {
		    strncpy(password, (buf+9), index(buf,'\n') - (buf + 9));
		}
		break;
	    default :
		break;
	}
    }
    fclose(config);
    fprintf(stderr, "Host: %s, User: %s, Password: %s\n",host, user, password);

    return 0;
}

static int
connection(char *host,char *uname,char *password)
{
    conn = mysql_init(NULL);
    if (conn == NULL) {
	fprintf(stderr, "Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
	exit(1);
    }

    if (mysql_real_connect(conn, host, user, password, mydata, 0, NULL, 0) == NULL) {
	printf("Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
	exit(1);
    }

    return 0;
}

static int
local_time(char *now)
{
    time_t t;
    struct tm *timenow;
    time(&t);
    timenow = localtime(&t);

    sprintf(now, "%4d-%02d-%02d %02d:%02d:%02d", 
	    1900+timenow->tm_year, timenow->tm_mon+1, timenow->tm_mday,
	    timenow->tm_hour, timenow->tm_min, timenow->tm_sec);
    return 0;
}

static int
session_remove(const char *uuid)
{
    char query[128];
    mytime now;;

    sprintf(query, "DELETE FROM Session WHERE uuid = '%s' ", uuid);

    LOG(query);
    if (mysql_query(conn, query)) {
	printf("Error %u: %s\n", mysql_errno(conn), mysql_error(conn));
	exit(1);
    }

    return 0;
}

static int
session_exist2(uint32_t user_id, char *uuid)
{
    char query[128];
    MYSQL_RES *result;
    MYSQL_ROW row;

    sprintf(query, "SELECT uuid FROM Session WHERE user_id = '%u' ", user_id);

    LOG(query);
    SAFE_QUERY_STORE(query, result, NO);
    if ((row = mysql_fetch_row(result))) {
	strcpy(uuid, row[0]);
	mysql_free_result(result);
	return 0;
    }

    mysql_free_result(result);
    return -1;
}

static int
gen_uuid(char *uuid)
{
    uuid_t idt;
    uuid_generate(idt);
    uuid_unparse(idt, uuid);
    return 0;
}

// do format and sercurity check.
// string pointer valid check.
static int
check_note(Note *note)
{
    // check the note.
    if (note->title&&strlen(note->title) > (1<<4)) {
	(note->title)[1<<4] = 0;
    }
    if (note->content&&strlen(note->content) > (1<<20)) {
	(note->content)[1<<20] = 0;
    }
    return 0;
}

static int
authortity_check(uint32_t user_id, uint64_t note_id)
{
    char query[256];
    char num[36];
    sprintf(query, "SELECT count(*) FROM Note WHERE id = %llu AND owner_id = %u ", note_id, user_id);
    if (get_number_mysql(query, num) >= 0) {// yours.
	return 0; }
    sprintf(query, "SELECT count(*) FROM Note WHERE id = %llu AND attribute&%u=0 ", note_id, PUBLIC);
    if (get_number_mysql(query, num) >= 0) {// public.
	return 0; }
    sprintf(query, "SELECT count(*) FROM Shared WHERE note_id = %llu AND user_id = %u ", note_id, user_id);
    if (get_number_mysql(query, num) >= 0) {// shared with you.
	return 0; }
    return -1;
}

static inline int
get_number_mysql(char *query, char *num)
{
    MYSQL_RES *result;
    MYSQL_ROW row;

    LOG(query);
    SAFE_QUERY_STORE(query, result, -1);

    if ((row = mysql_fetch_row(result))) {
	strcpy(num, row[0]);
	mysql_free_result(result);
	return 0;
    }
    mysql_free_result(result);
    return -1;
}

static inline int
user_copy_mysql(char *query, ArrayTable *dest)
{
    MYSQL_RES *result;
    MYSQL_ROW row;

    LOG(query);
    SAFE_QUERY_STORE(query, result, -1);

    dest->size = mysql_num_rows(result);
    SAFE_MALLOC(dest->ptr, dest->size*sizeof(User));
    uint32_t i = 0;
    while ((row = mysql_fetch_row(result))) {
	((User *)(dest->ptr))[i].id = atoll(row[0]);
	((User *)(dest->ptr))[i].nickname = strdup(row[1]);
	((User *)(dest->ptr))[i].mail = strdup(row[2]);
	((User *)(dest->ptr))[i].passwd = strdup(row[3]);
	((User *)(dest->ptr))[i].time_reg = strdup(row[4]);
	((User *)(dest->ptr))[i].time_lastlogin = strdup(row[5]);
	((User *)(dest->ptr))[i].time_lastupdate = strdup(row[6]);
	((User *)(dest->ptr))[i].attribute = atoi(row[7]);
	i++;
    }
    mysql_free_result(result);
    return 0;
}

static inline int
note_copy_mysql(char *query, ArrayTable *dest)
{
    MYSQL_RES *result;
    MYSQL_ROW row;

    LOG(query);
    SAFE_QUERY_STORE(query, result, -1);

    dest->size = mysql_num_rows(result);
    SAFE_MALLOC(dest->ptr, dest->size*sizeof(Note));
    uint32_t i = 0;
    while ((row = mysql_fetch_row(result))) {
	((Note *)(dest->ptr))[i].id = atoll(row[0]); 
	((Note *)(dest->ptr))[i].owner_id = atoll(row[1]); 
	((Note *)(dest->ptr))[i].uuid = strdup(row[2]); 
	((Note *)(dest->ptr))[i].title = strdup(row[3]); 
	((Note *)(dest->ptr))[i].content = strdup(row[4]); 
	((Note *)(dest->ptr))[i].time_create = strdup(row[5]); 
	((Note *)(dest->ptr))[i].time_lastupdate = strdup(row[6]); 
	((Note *)(dest->ptr))[i].version = atoll(row[7]); 
	((Note *)(dest->ptr))[i].attribute = atoll(row[8]); 
	i++;
    }
    mysql_free_result(result);
    return 0;
}

static inline int
tag_copy_mysql(char *query, ArrayTable *dest)
{
    MYSQL_RES *result;
    MYSQL_ROW row;

    LOG(query);
    SAFE_QUERY_STORE(query, result, -1);

    dest->size = mysql_num_rows(result);
    SAFE_MALLOC(dest->ptr, dest->size*sizeof(Tag));
    uint32_t i = 0;
    while ((row = mysql_fetch_row(result))) {
	((Tag *)(dest->ptr))[i].name = strdup(row[0]); 
	((Tag *)(dest->ptr))[i].note_id = atoll(row[1]); 
	((Tag *)(dest->ptr))[i].owner_id = atoll(row[2]); 
	((Tag *)(dest->ptr))[i].time_create = strdup(row[3]); 
	((Tag *)(dest->ptr))[i].count_ref = atoll(row[4]); 
	i++;
    }
    mysql_free_result(result);
    return 0;
}
static inline int
result_rows(const char *query)
{
    MYSQL_RES *result;
    LOG(query);
    SAFE_QUERY_STORE(query, result, 0);
    uint32_t num = mysql_num_rows(result);
    mysql_free_result(result);
    return num;
}
